package edu.ncsu.csc.ip.server.protocol;

import java.net.DatagramPacket;
import edu.ncsu.csc.ip.common.Protocol;

/**
 * Class <code></code>
 * 
 * 
 * 
 * @author Moin Ayazifar <mayazif@ncsu.edu>
 * @author Steven Elliott <sdelliot@ncsu.edu>
 * 
 */

public class ProtocolServerImpl implements Protocol {


	private DatagramPacket packet;
	private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
	private static final int ACK_SIZE = 8;
	
	public ProtocolServerImpl(DatagramPacket receivePacket) {
		this.setPacket(receivePacket);
	}

	/** THE MESSAGE REGISTER */
	public static final String FILESIZE = "FILESIZE";
	public static final String ACK = "AAAA";
	public static final String DATA = "5555";
	public static final String FIN = "FFFF";

	/**
	 * @param cookie
	 *            the cookie to set
	 */

	public String parseMessage(byte[] buf) {
		String message = asHex(buf);
		if (message != null && message.substring(0, 16).toUpperCase().endsWith(FIN)) {
			return FIN;
		} else {
			return "";
		}
	}

	public byte[] removeNulls(byte[] data){
		int i = data.length;
		while (i-- > 0 && data[i] == 0) {}
		byte[] trimedData = new byte[i+1];
		System.arraycopy(data, 0, trimedData, 0, i+1);
		return trimedData;
	}

	// This method Converts byte[] to hex string
	public static String asHex(byte[] buf) {
		char[] chars = new char[2 * buf.length];
		for (int i = 0; i < buf.length; ++i) {
			chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
			chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
		}
		return new String(chars);
	}

	public boolean isChecksumCorrect(byte[] bs){
		int checksum = ~calculateChecksum(bs);
		String checksumString = Integer.toBinaryString(checksum);
		if (checksumString.equals("1111111111111111")) {
			return true;
		} else {
			return false;
		}
	}
	
	public int calculateChecksum(byte[] bs) {

		if(bs == null){
			System.out.println("No data");
			return 0;
		}
		//convert byte string to hex
		String data = asHex(bs);
		if(data.length()%4 != 0){
			System.out.println("Not in 16 bit chunks");
			return 0;
		}
		String [] myList = new String[data.length()/4];
		int index = 0;
		int count = 0;
		while (index < data.length()) {
			myList[count] = data.substring(index, Math.min(index+4,data.length()));
		    index += 4;
		    count += 1;
		}
		
		int sum = 0;
		for (int i = 0; i < myList.length; i++) {
			sum += Integer.parseInt(myList[i], 16);
			
			// Wrap around digests
			 while(sum > Integer.parseInt("1111111111111111", 2)){
				 sum = sum - Integer.parseInt("10000000000000000", 2);
				 sum += 1;
			 }
		}
		return ~sum;
	}

	public byte[] makeACK(byte[] sequenceNumber){
		byte[] buf = new byte[ACK_SIZE];
		
		for ( int i = 0; i < 4; i++ ){
			buf[i] = sequenceNumber[i];
		}
		
		/* Zeros takes 2 bytes = 16 bits */
		buf[4] = new Integer(0).byteValue();
		buf[5] = new Integer(0).byteValue();

		/* ACK sequence takes 2 bytes = 16 bits */
		buf[6] = new Integer((1 << 7)|(0 << 6)|(1 << 5)|(0 << 4)|(1 << 3)|(0 << 2)|(1 << 1)|0).byteValue();
		buf[7] = new Integer((1 << 7)|(0 << 6)|(1 << 5)|(0 << 4)|(1 << 3)|(0 << 2)|(1 << 1)|0).byteValue();
		
		return buf;
	}
	
	public long getSeqNum(byte[] bs){
		if(bs == null){
			System.out.println("No data");
			return 0;
		}
		//convert byte string to hex
		String data = asHex(bs);
		long sequenceNumber = 0;
		try{
			sequenceNumber = Long.parseLong(data.substring(0,8),16);
		} catch(NumberFormatException e){
			System.out.println("Sequence Number malformated");
			return 0;
		}
		return sequenceNumber;
	}

	@Override
	public DatagramPacket getPacket() {
		return packet;
	}

	@Override
	public void setPacket(DatagramPacket packet) {
		this.packet = packet;
	}

}
